/*
Copyright (C) 2010 Ronie Salgado

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "stdafx.h"
#include "QPropTexture.h"
#include "QTexturePanel.h"
#include "QooleDoc.h"
#include "QMainFrame.h"
#include "QooleId.h"
#include "FloatValidator.h"

const float OffsetIncrStep = 1.0f;
const float ScaleIncrStep = 0.05f;
const float RotIncrStep = 5.0f;

static wxPanel *CreateArrowButtons(wxWindow *parent, wxBitmap &bleft, wxBitmap &bright, wxBitmap &bup, wxBitmap &bdn,
		int left, int right, int up, int down)
{
	wxPanel *ret = new wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(50, 50));

	wxSize arrowSize(20, 20);
	wxButton *leftButton = new wxBitmapButton(ret, left, bleft,
			wxPoint(5, 15), arrowSize);
	wxButton *rightButton = new wxBitmapButton(ret, right, bright,
			wxPoint(45, 15), arrowSize);
	wxButton *upButton = new wxBitmapButton(ret, up, bup,
			wxPoint(25, 5), arrowSize);
	wxButton *downButton = new wxBitmapButton(ret, down, bdn,
			wxPoint(25, 25), arrowSize);
	return ret;
}

BEGIN_EVENT_TABLE(QPropTexture, QPropSheetPage)
	EVT_ACTIVATE(QPropTexture::OnActivate)
	EVT_RADIOBUTTON(QID_PROPTEX_SELECT_ALL_FACES, QPropTexture::OnOptionSelectAll)
	EVT_RADIOBUTTON(QID_PROPTEX_SELECT_FACE, QPropTexture::OnOptionSelectFace)
	EVT_BUTTON(QID_PROPTEX_PREV_FACE, QPropTexture::OnSelectPrevFace)
	EVT_BUTTON(QID_PROPTEX_NEXT_FACE, QPropTexture::OnSelectNextFace)
	EVT_BUTTON(QID_PROPTEX_RESET, QPropTexture::OnReset)
	EVT_BUTTON(QID_PROPTEX_AUTO_ALIGN, QPropTexture::OnAutoAlign)
	EVT_BUTTON(QID_PROPTEX_OFFSET_X_POS, QPropTexture::OnXOffsetIncr)
	EVT_BUTTON(QID_PROPTEX_OFFSET_X_NEG, QPropTexture::OnXOffsetDecr)
	EVT_BUTTON(QID_PROPTEX_OFFSET_Y_POS, QPropTexture::OnYOffsetIncr)
	EVT_BUTTON(QID_PROPTEX_OFFSET_Y_NEG, QPropTexture::OnYOffsetDecr)
	EVT_BUTTON(QID_PROPTEX_SCALE_X_POS, QPropTexture::OnXScaleIncr)
	EVT_BUTTON(QID_PROPTEX_SCALE_X_NEG, QPropTexture::OnXScaleDecr)
	EVT_BUTTON(QID_PROPTEX_SCALE_Y_POS, QPropTexture::OnYScaleIncr)
	EVT_BUTTON(QID_PROPTEX_SCALE_Y_NEG, QPropTexture::OnYScaleDecr)
	EVT_BUTTON(QID_PROPTEX_ROT_POS, QPropTexture::OnRotIncr)
	EVT_BUTTON(QID_PROPTEX_ROT_NEG, QPropTexture::OnRotDecr)
	EVT_TEXT(QID_PROPTEX_OFFSET_X, QPropTexture::OnEditChanged)
	EVT_TEXT(QID_PROPTEX_OFFSET_Y, QPropTexture::OnEditChanged)
	EVT_TEXT(QID_PROPTEX_SCALE_X, QPropTexture::OnEditChanged)
	EVT_TEXT(QID_PROPTEX_SCALE_Y, QPropTexture::OnEditChanged)
	EVT_TEXT(QID_PROPTEX_ROT, QPropTexture::OnEditChanged)
END_EVENT_TABLE()

QPropTexture::QPropTexture(wxWindow *parent)
	: QPropSheetPage(parent)
{
	// Set the initial values.
	xoffset = yoffset = 0.0f;
	xscale = yscale = 1.0f;
	rotAng = 0.0f;
	selectAll = true;
	selectFace = !selectAll;
	controlsEnabled = true;
	transferring = false;
	focusedControl = NULL;
	active = false;

	// Create the panel layout.
	wxSizer *topSizer = new wxBoxSizer(wxVERTICAL);

	// Create the upper layout
	wxSizer *upperHoriz = new wxBoxSizer(wxHORIZONTAL);
	topSizer->Add(upperHoriz, 1, wxALL|wxEXPAND, 5);

	// Create the texture column and the face selection.
	wxSizer *column = new wxBoxSizer(wxVERTICAL);
	upperHoriz->Add(column, 1, wxEXPAND);

	textureControl = new QTexturePanel(this, wxID_ANY);
	column->Add(textureControl, 1, wxALL|wxEXPAND, 5);
	column->AddSpacer(10);

	selectAllButton = new wxRadioButton(this, QID_PROPTEX_SELECT_ALL_FACES, _("Select All"));
	column->Add(selectAllButton);
	selectFaceButton= new wxRadioButton(this, QID_PROPTEX_SELECT_FACE, _("Select Face"));
	column->Add(selectFaceButton);

	// Create the buttons.
	wxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
	column->Add(buttonSizer, 0, wxALL|wxALIGN_CENTER, 5);

	prevFaceButton = new wxButton(this, QID_PROPTEX_PREV_FACE, _("Prev"));
	nextFaceButton = new wxButton(this, QID_PROPTEX_NEXT_FACE, _("Next"));
	buttonSizer->Add(prevFaceButton, 0, wxRIGHT, 2);
	buttonSizer->Add(nextFaceButton, 0, wxLEFT, 2);

	// Create the offset, scale, angle and texture lock columns.
	column = new wxBoxSizer(wxVERTICAL);
	upperHoriz->Add(column, 1, wxEXPAND);
	wxSizer *row = new wxBoxSizer(wxHORIZONTAL);
	column->Add(row, 0, wxEXPAND);

	wxSizer *labels = new wxBoxSizer(wxVERTICAL);
	wxSizer *controls = new wxBoxSizer(wxVERTICAL);
	wxSizer *buttons = new wxBoxSizer(wxVERTICAL);
	row->Add(labels, 0, wxEXPAND);
	row->Add(controls, 1, wxEXPAND);
	row->Add(buttons, 0, wxEXPAND);

	// Offset, scale, angle.
	wxStaticText *label = new wxStaticText(this, wxID_ANY, _("X Offset"));
	xoffsetCtrl = new wxTextCtrl(this, QID_PROPTEX_OFFSET_X,
			wxT("0"), wxDefaultPosition, wxDefaultSize, 0,
			FloatValidator(&xoffset));
	labels->Add(label, 0, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 8);
	controls->Add(xoffsetCtrl, 1, wxALL|wxEXPAND, 2);

	label = new wxStaticText(this, wxID_ANY, _("Y Offset"));
	yoffsetCtrl = new wxTextCtrl(this, QID_PROPTEX_OFFSET_Y,
			wxT("0"), wxDefaultPosition, wxDefaultSize, 0,
			FloatValidator(&yoffset));
	labels->Add(label, 0, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 8);
	controls->Add(yoffsetCtrl, 1, wxALL|wxEXPAND, 2);

	label = new wxStaticText(this, wxID_ANY, _("X Scale"));
	xscaleCtrl = new wxTextCtrl(this, QID_PROPTEX_SCALE_X,
			wxT("1"), wxDefaultPosition, wxDefaultSize, 0,
			FloatValidator(&xscale));
	labels->Add(label, 0, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 8);
	controls->Add(xscaleCtrl, 1, wxALL|wxEXPAND, 2);

	label = new wxStaticText(this, wxID_ANY, _("Y Scale"));
	yscaleCtrl = new wxTextCtrl(this, QID_PROPTEX_SCALE_Y,
			wxT("1"), wxDefaultPosition, wxDefaultSize, 0,
			FloatValidator(&yscale));
	labels->Add(label, 0, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 8);
	controls->Add(yscaleCtrl, 1, wxALL|wxEXPAND, 2);

	label = new wxStaticText(this, wxID_ANY, _("Rot Ang"));
	rotAngCtrl = new wxTextCtrl(this, QID_PROPTEX_ROT,
			wxT("0"), wxDefaultPosition, wxDefaultSize, 0,
			FloatValidator(&rotAng));
	labels->Add(label, 0, wxALL|wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 8);
	controls->Add(rotAngCtrl, 1, wxALL|wxEXPAND, 2);

	texLockCheck = new wxCheckBox(this, QID_PROPTEX_LOCK, _("Texture Lock"));
	column->Add(texLockCheck);

	// Create the arrows buttons.
	wxBitmap leftBitmap = wxXmlResource::Get()->LoadBitmap(wxT("imageLeft"));
	wxBitmap rightBitmap = wxXmlResource::Get()->LoadBitmap(wxT("imageRight"));
	wxBitmap upBitmap = wxXmlResource::Get()->LoadBitmap(wxT("imageUp"));
	wxBitmap downBitmap = wxXmlResource::Get()->LoadBitmap(wxT("imageDown"));

	offsetArrows = CreateArrowButtons(this,
			leftBitmap, rightBitmap, upBitmap, downBitmap,
			QID_PROPTEX_OFFSET_X_NEG, QID_PROPTEX_OFFSET_X_POS,
			QID_PROPTEX_OFFSET_Y_POS, QID_PROPTEX_OFFSET_Y_NEG);
	buttons->Add(offsetArrows, 0, wxALL|wxALIGN_CENTER, 5);

	scaleArrows = CreateArrowButtons(this,
				leftBitmap, rightBitmap, upBitmap, downBitmap,
				QID_PROPTEX_SCALE_X_NEG, QID_PROPTEX_SCALE_X_POS,
				QID_PROPTEX_SCALE_Y_POS, QID_PROPTEX_SCALE_Y_NEG);
	buttons->AddSpacer(7);
	buttons->Add(scaleArrows, 0, wxALL|wxALIGN_CENTER, 5);

	wxSizer *rotSizer = new wxBoxSizer(wxHORIZONTAL);
	buttons->AddSpacer(8);
	buttons->Add(rotSizer, 0, wxALL|wxALIGN_CENTER, 5);
	rotDecButton = new wxBitmapButton(this, QID_PROPTEX_ROT_NEG, leftBitmap);
	rotIncrButton = new wxBitmapButton(this, QID_PROPTEX_ROT_POS, rightBitmap);
	rotSizer->Add(rotDecButton);
	rotSizer->Add(rotIncrButton);

	// Create more buttons.
	buttonSizer = new wxBoxSizer(wxHORIZONTAL);
	column->AddStretchSpacer(1);
	column->Add(buttonSizer, 0, wxALL|wxALIGN_CENTER, 5);

	autoAlignButton = new wxButton(this, QID_PROPTEX_AUTO_ALIGN, _("Auto Align"));
	resetButton = new wxButton(this, QID_PROPTEX_RESET, _("Reset"));
	buttonSizer->Add(autoAlignButton, 0, wxRIGHT, 2);
	buttonSizer->Add(resetButton, 0, wxLEFT, 2);

	SetSizer(topSizer);
	SetAutoLayout(true);
	TransferDataToWindow();
}

QPropTexture::~QPropTexture()
{
}

void QPropTexture::OnActivate(wxActivateEvent &event)
{
	if(event.GetActive())
	{
		// Activate.
		active = true;
		Selector *selector = &GetMainFrame()->GetSelector();

		// Reenable face selection rendering if a face was already selected.
		if (selector->GetSelectFaceIndex() != -1)
		{
			RenderSelectedFace(true);
			QDocHint hint;
			hint.flags = DUAV_QVIEWS;
			GetDocument()->UpdateAllViews(NULL, &hint);
		}

		// Remember the old attributes
		if(focusedControl == NULL)
		{
			focusedControl = this;
			oldXOffset = xoffset;
			oldYOffset = yoffset;
			oldXScale = xscale;
			oldYScale = yscale;
			oldRotAng = rotAng;
		}
	}
	else
	{
		// Deactivate.
		active = false;

		// Commit the changes
		if(focusedControl &&
			(oldXOffset != xoffset || oldYOffset != yoffset ||
			 oldRotAng != rotAng || oldXScale != xscale ||
			 oldYScale != yscale))
		{
			CommitAttributes();
			focusedControl = NULL;
		}

		// Disable face selection rendering in qviews.
		if(GetDocument() != NULL && selectFace)
		{
			RenderSelectedFace(false);
			QDocHint hint;
			hint.flags = DUAV_QVIEWS;
			GetDocument()->UpdateAllViews(NULL, &hint);
		}
	}
}

void QPropTexture::OnUpdate(wxView* sender, wxObject* hint)
{
	// Read the hint, if available.
	int flags = 0;
	Object *scope = NULL;
	if(hint && hint->IsKindOf(CLASSINFO(QDocHint)))
	{
		QDocHint *qhint = static_cast<QDocHint*> (hint);
		flags = qhint->flags;
		scope = qhint->scope;
	}

	if(flags & DUAV_NOQPROPPAGES || !active)
		return;

	// Disable controls.
	DisableControls();

	// Check the scope.
	Selector *selector = &GetMainFrame()->GetSelector();
	Object *editScope = selector->GetScopePtr();
	if(scope != NULL && scope != editScope &&
		!scope->IsMyAncestor(*editScope))
		return;

	unsigned int filter = DUAV_OBJSSEL | // DUAV_SCOPECHANGED |
			  DUAV_OBJSMODSTRUCT | DUAV_OBJTEXMODATTRIB;
	if(scope != NULL && !(flags & filter))
		return; // Changes don't affect us.

	// Check if we need to disable/enable controls.
	if(selector->GetNumMSelectedObjects() == 1 &&
		selector->GetMSelectedObjects()[0].GetPtr()->HasBrush())
	{
		// Enable the face selection controls.
		selectAllButton->Enable(true);
		selectFaceButton->Enable(true);
	}
	else
	{
		// Disable the face selection controls.
		selectAllButton->Enable(false);
		selectFaceButton->Enable(false);
	}

	if(selector->GetSelectFaceIndex() == -1)
	{
		wxCommandEvent ev;
		OnOptionSelectAll(ev);
	}
	else
	{
		// Only occurs when switching tabs with pre-selected face.
		//  and when undo/redo involves entire brush <-> face slct change.
		selectAllButton->Enable(true);
		selectFaceButton->Enable(true);
		selectFace = true;
		selectAll = !selectFace;
		RenderSelectedFace(true);
		OnSelectNewFace();
	}
}

QView *QPropTexture::Get3DView()
{
	// Select a 3d view.
	wxList &views = GetDocument()->GetViews();
	wxList::iterator it = views.begin();
	for(; it != views.end(); it++)
	{
		wxObject *view = *it;
		if(!view || !view->IsKindOf(CLASSINFO(QView)))
			continue;

		QView *qview = static_cast<QView*> (view);
		if(qview->GetViewType() == QView::VT_3D)
			return qview;
	}

	return NULL;
}

void QPropTexture::OnOptionSelectAll(wxCommandEvent &event)
{
	selectAllButton->SetValue(true);
	selectFaceButton->SetValue(false);
	prevFaceButton->Enable(false);
	nextFaceButton->Enable(false);

	textureControl->SetTexture(NULL);

	if(selectAll)
		return;

	selectAll = true;
	selectFace = !selectAll;

	// Disable face selection rendering in views.
	RenderSelectedFace(false);

	Selector *selector = &GetMainFrame()->GetSelector();
	selector->SelectFaceIndex(-1);

	QDocHint hint;
	hint.flags = DUAV_QVIEWS;
	GetDocument()->UpdateAllViews(NULL, &hint);

	// Disable controls.
	DisableControls();
}

void QPropTexture::OnOptionSelectFace(wxCommandEvent &event)
{
	selectAllButton->SetValue(false);
	selectFaceButton->SetValue(true);
	prevFaceButton->Enable(true);
	nextFaceButton->Enable(true);

	if(selectFace)
		return;

	selectFace = true;
	selectAll = !selectFace;

	// Enable face selection rendering in views.
	RenderSelectedFace(true);

	// Select the first face.
	Selector *selector = &GetMainFrame()->GetSelector();
	QView *qview = Get3DView();

	Vector3d viewVec;
	if(qview && qview->GetViewType() == QView::VT_3D)
	{
		// Use the 3d view for face alignment.
		Object *selectedObject = selector->GetMSelectedObjects()[0].GetPtr();
		Vector3d posVec;
		SphrVector oriVec;
		float zoomVal;

		viewVec = selectedObject->GetPosition();
		qview->GetViewState(posVec, oriVec, zoomVal);
		viewVec.SubVector(posVec);
	}
	else
	{
		// Just use the top view.
		viewVec.NewVector(0.0f, 0.0f, -1.0f);
	}

	selector->ResetSelectFace(viewVec);
	OnSelectNewFace();

	QDocHint hint;
	hint.flags = DUAV_QVIEWS;
	GetDocument()->UpdateAllViews(NULL, &hint);

	// Disable controls.
	DisableControls();
}

void QPropTexture::OnSelectNewFace()
{
	// Get the new texture info and update the controls.
	Selector *selector = &GetMainFrame()->GetSelector();
	ASSERT(selector->GetNumMSelectedObjects() == 1);

	Object *selectedObject = selector->GetMSelectedObjects()[0].GetPtr();
	ASSERT(selectedObject->HasBrush());


	Geometry *brush = &selectedObject->GetBrush();
	int faceIndex = selector->GetSelectFaceIndex();
	FaceTex *faceTex = brush->GetFaceTexturePtr(faceIndex);

	// TODO: Change the offset into a floating point value.
	int xoff, yoff;
	faceTex->GetTInfo(xoff, yoff, rotAng, xscale, yscale);
	xoffset = xoff;
	yoffset = yoff;

	textureControl->SetTexture(faceTex->GetTexture());

	TransferDataToWindow();
}

void QPropTexture::OnSelectPrevFace(wxCommandEvent &event)
{
	ASSERT(selectFace);

	Selector *selector = &GetMainFrame()->GetSelector();
	QView *qview = Get3DView();
	Vector3d viewVec;

	if(qview)
	{
		// Use the 3d view for face alignment calc.
		Object *selectedObject = selector->GetMSelectedObjects()[0].GetPtr();
		Vector3d posVec;
		SphrVector oriVec;
		float zoomVal;

		viewVec = selectedObject->GetPosition();
		qview->GetViewState(posVec, oriVec, zoomVal);
		viewVec.SubVector(posVec);
	}
	else
	{
		// Just use the top view.
		viewVec.NewVector(0.0f, 0.0f, -1.0f);
	}

	selector->BackwardSelectFace(viewVec);
	OnSelectNewFace();


	QDocHint hint;
	hint.flags = DUAV_QVIEWS;
	GetDocument()->UpdateAllViews(NULL, &hint);
}

void QPropTexture::OnSelectNextFace(wxCommandEvent &event)
{
	ASSERT(selectFace);

	Selector *selector = &GetMainFrame()->GetSelector();
	QView *qview = Get3DView();
	Vector3d viewVec;

	if(qview)
	{
		// Use the 3d view for face alignment calc.
		Object *selectedObject = selector->GetMSelectedObjects()[0].GetPtr();
		Vector3d posVec;
		SphrVector oriVec;
		float zoomVal;

		viewVec = selectedObject->GetPosition();
		qview->GetViewState(posVec, oriVec, zoomVal);
		viewVec.SubVector(posVec);
	}
	else
	{
		// Just use the top view.
		viewVec.NewVector(0.0f, 0.0f, -1.0f);
	}

	selector->ForwardSelectFace(viewVec);
	OnSelectNewFace();


	QDocHint hint;
	hint.flags = DUAV_QVIEWS;
	GetDocument()->UpdateAllViews(NULL, &hint);
}

void QPropTexture::DisableControls()
{
	if(selectAll)
	{
		controlsEnabled = false;

		xoffset = yoffset = 0.0f;
		xscale = yscale = 1.0f;
		rotAng = 0.0f;
	}
	else
	{
		controlsEnabled = true;
	}

	rotDecButton->Enable(controlsEnabled);
	rotIncrButton->Enable(controlsEnabled);
	offsetArrows->Enable(controlsEnabled);
	scaleArrows->Enable(controlsEnabled);

	xoffsetCtrl->Enable(controlsEnabled);
	yoffsetCtrl->Enable(controlsEnabled);
	xscaleCtrl->Enable(controlsEnabled);
	yscaleCtrl->Enable(controlsEnabled);
	rotAngCtrl->Enable(controlsEnabled);

	Selector *selector = &GetMainFrame()->GetSelector();
	wxCheckBoxState lockState = wxCHK_UNCHECKED;
	bool flag = controlsEnabled;
	if(!flag && selector->GetNumMSelectedObjects() == 1 &&
		selector->GetMSelectedObjects()[0].GetPtr()->HasBrush())
	{
		flag = true;

		// Iterate through the faces of the brush to check texture lock.
		Object *object = selector->GetMSelectedObjects()[0].GetPtr();
		Geometry *brush = &object->GetBrush();
		FaceTex *faceTex;
		int numFaces = brush->GetNumFaces();
		bool unlock = false, lock = false;
		for(int i = 0; i < numFaces; i++)
		{
			faceTex = brush->GetFaceTexturePtr(i);
			if(faceTex->IsTexLocked())
				lock = true;
			else
				unlock = true;

			// Optimization.
			if(lock && unlock)
				break;
		}

		if(unlock && lock)
			lockState = wxCHK_UNDETERMINED;
		else if(!lock)
			lockState = wxCHK_UNCHECKED;
		else
			lockState = wxCHK_CHECKED;
	}
	else if(selectFace)
	{
		lockState = texLockCheck->Get3StateValue();
	}

	texLockCheck->Enable(flag);
	texLockCheck->Set3StateValue(lockState);

	autoAlignButton->Enable(flag);
	resetButton->Enable(flag);
}

void QPropTexture::RenderSelectedFace(bool visible)
{
	QooleDoc *doc = GetDocument();

	// Iterate through each one of the views
	wxList &views = doc->GetViews();
	wxList::iterator it = views.begin();
	for(; it != views.end(); it++)
	{
		wxObject *view = *it;
		if(!view || !view->IsKindOf(CLASSINFO(QView)))
			continue;

		QView *qview = static_cast<QView*> (view);
		qview->RenderSelectedFace(visible);
	}
}

// X Offset
void QPropTexture::OnXOffsetIncr(wxCommandEvent &event)
{
	// Increase the value.
	xoffset += OffsetIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

void QPropTexture::OnXOffsetDecr(wxCommandEvent &event)
{
	// Decrease the value.
	xoffset -= OffsetIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

// Y Offset
void QPropTexture::OnYOffsetIncr(wxCommandEvent &event)
{
	// Increase the value.
	yoffset += OffsetIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

void QPropTexture::OnYOffsetDecr(wxCommandEvent &event)
{
	// Decrease the value.
	yoffset -= OffsetIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

// X Scale
void QPropTexture::OnXScaleIncr(wxCommandEvent &event)
{
	// Increase the value.
	xscale += ScaleIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

void QPropTexture::OnXScaleDecr(wxCommandEvent &event)
{
	// Decrease the value.
	xscale -= ScaleIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

// Y Scale
void QPropTexture::OnYScaleIncr(wxCommandEvent &event)
{
	// Increase the value.
	yscale += ScaleIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

void QPropTexture::OnYScaleDecr(wxCommandEvent &event)
{
	// Decrease the value.
	yscale -= ScaleIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

// Rotation angle
void QPropTexture::OnRotIncr(wxCommandEvent &event)
{
	// Increase the value.
	rotAng += RotIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

void QPropTexture::OnRotDecr(wxCommandEvent &event)
{
	// Decrease the value.
	rotAng -= RotIncrStep;

	// Update the control value.
	TransferDataToWindow();
	UpdateDoc();
}

void QPropTexture::UpdateDoc(bool updateViews)
{
	Selector *selector = &GetMainFrame()->GetSelector();
	ASSERT(selector->GetNumMSelectedObjects() == 1);

	if(selector->GetSelectFaceIndex() == -1)
		return;

	Object *selectedObject = selector->GetMSelectedObjects()[0].GetPtr();
	QooleDoc *doc = GetDocument();
	doc->TextureManipulate(*selectedObject, selector->GetSelectFaceIndex(),
							xoffset, yoffset, rotAng, xscale, yscale,
							texLockCheck->GetValue(), false);

	if(updateViews)
	{
		QDocHint hint;
		hint.flags = DUAV_QVIEWS | DUAV_OBJTEXMODATTRIB;
		hint.scope = selectedObject;
		doc->UpdateAllViews(NULL, &hint);
	}
}

void QPropTexture::CommitAttributes()
{
	Selector *selector = &GetMainFrame()->GetSelector();
	ASSERT(selector->GetNumMSelectedObjects() == 1);
	ASSERT(selector->GetMSelectedObjects()[0].GetPtr()->HasBrush());

	int faceIndex = selector->GetSelectFaceIndex();
	ASSERT(faceIndex != -1);

	OpTextureFaceManip *op =
			new OpTextureFaceManip(faceIndex, oldXOffset, oldYOffset,
					oldRotAng, oldXScale, oldYScale, texLockCheck->GetValue());
	GetMainFrame()->CommitOperation(*op);
}

void QPropTexture::OnEditChanged(wxCommandEvent &event)
{
	// Avoid reading incomplete transfer.
	if(transferring)
		return;

	Selector *selector = &GetMainFrame()->GetSelector();
	if(selector->GetNumMSelectedObjects() != 1)
		return;

	TransferDataFromWindow();
	UpdateDoc();
}

bool QPropTexture::TransferDataToWindow()
{
	ASSERT(!transferring);
	transferring = true;
	bool ret = wxPanel::TransferDataToWindow();
	transferring = false;
	return ret;
}

// Auto align texture.
void QPropTexture::OnAutoAlign(wxCommandEvent &event)
{
	if(!selectFace)
	{
		AutoAlignBrush();
		return;
	}

	// Remember the old data.
	TransferDataFromWindow();
	oldXOffset = xoffset;
	oldYOffset = yoffset;
	oldXScale = xscale;
	oldYScale = yscale;
	oldRotAng = rotAng;
	bool oldTexLock = texLockCheck->GetValue();

	Selector *selector = &GetMainFrame()->GetSelector();
	int faceIndex = selector->GetSelectFaceIndex();
	ASSERT(faceIndex != -1);

	AutoAlignFace(faceIndex, xoffset, yoffset, rotAng, xscale, yscale);
	TransferDataToWindow();
	UpdateDoc(false);

	OpTextureFaceManip *op =
		new OpTextureFaceManip(selector->GetSelectFaceIndex(),
							   oldXOffset, oldYOffset, oldRotAng,
							   oldXScale, oldYScale, oldTexLock);
	GetMainFrame()->CommitOperation(*op);
}

void QPropTexture::AutoAlignFace(int faceIndex, float &xoff, float &yoff,
		float &rotAng, float &xScl, float &yScl)
{
	// Get some ptrs.
	Selector *selector = &GetMainFrame()->GetSelector();
	ASSERT(selector->GetNumMSelectedObjects() == 1);

	Object *pObjNode = (selector->GetMSelectedObjects())[0].GetPtr();
	Geometry *pBrush = &(pObjNode->GetBrush());
	ASSERT(faceIndex >= 0 && faceIndex < pBrush->GetNumFaces());
	FaceTex *pFaceTex = pBrush->GetFaceTexturePtr(faceIndex);
	const GPolygon *pGPoly = &(pBrush->GetFace(faceIndex));
	Plane plane((pBrush->GetPlaneList())[faceIndex].GetPlane());

	// Derive transformation matrix.
	Matrix44 trans;
	trans.SetIdentity();

	if (!pFaceTex->IsTexLocked())
	{
		while (!pObjNode->IsRoot()) {
			pObjNode->CalTransSpaceMatrix(trans);
			pObjNode = pObjNode->GetParentPtr();
		}
		trans.Transform(plane);
	}

	// Iter through face vertices and find bounding vectors.
	int i, vIndx;
	Vector3d pVec, nVec(plane.GetNorm());
	float s, t, sMin, sMax, tMin, tMax;

	for(i = 0; i < pGPoly->GetNumSides(); i++) {
		vIndx = (pGPoly->GetSide(i)).GetStartPoint();
		trans.Transform(pVec, pBrush->GetVertex(vIndx));
		FaceTex::Project2D(nVec, pVec, s, t);

		if (i == 0) {
			sMin = sMax = s;
			tMin = tMax = t;
			continue;
		}

		sMin = Min(s, sMin);
		sMax = Max(s, sMax);
		tMin = Min(t, tMin);
		tMax = Max(t, tMax);
	}

	// Define basis vectors.
	int width, height;
	Vector3d oVec, sVec, tVec;
	FaceTex tmpFace(wxT(""));

	pFaceTex->GetTDim(width, height);
	tmpFace.ST2XYZ(plane, sMin, tMin, oVec);
	tmpFace.ST2XYZ(plane, (sMax - sMin) / width + sMin, tMin, sVec);
	tmpFace.ST2XYZ(plane, sMin, (tMax - tMin) / height + tMin, tVec);

	// Derive new alignment settings.
	float xOffset, yOffset, rAng, xScale, yScale;
	FaceTex::DeriveCoordSys(oVec, sVec, tVec, nVec,
							xOffset, yOffset, rAng,
							xScale, yScale);

	// TODO: Check the rounding for errors.
	xoff = ROUNDI(xOffset);
	yoff = -ROUNDI(yOffset);
	rAng = RAD2DEG(rAng);
	rotAng = ROUND3(rAng);
	xScl = ROUND3(xScale);
	yScl = ROUND3(yScale);
}

void QPropTexture::AutoAlignBrush()
{
	// TODO: Clean up this code.
	Selector *selector = &GetMainFrame()->GetSelector();
	ASSERT(selector->GetNumMSelectedObjects() == 1);

	Object *pObjNode = (selector->GetMSelectedObjects())[0].GetPtr();
	Geometry *pBrush = &(pObjNode->GetBrush());
	FaceTex *pFaceTex;

	int numFaces = pBrush->GetNumFaces();

	// TODO: Change the offsets to floats.
	int *pOldXOffs = new int[numFaces];
	int *pOldYOffs = new int[numFaces];
	float *pOldRotAngs = new float[numFaces];
	float *pOldXScls = new float[numFaces];
	float *pOldYScls = new float[numFaces];
	bool *pOldTexLocks = new bool[numFaces];

	float xOff, yOff;
	float rotAng, xScl, yScl;

	for(int i = 0; i < numFaces; i++) {
		pFaceTex = pBrush->GetFaceTexturePtr(i);
		pFaceTex->GetTInfo(pOldXOffs[i], pOldYOffs[i], pOldRotAngs[i],
						   pOldXScls[i], pOldYScls[i]);
		pOldTexLocks[i] = pFaceTex->IsTexLocked();

		AutoAlignFace(i, xOff, yOff, rotAng, xScl, yScl);

		pFaceTex->SetTInfo(xOff, yOff, rotAng, xScl, yScl);
	}

	// Manual update the QViews, skipping self.
	QDocHint hint;
	hint.flags = DUAV_QVIEWS | DUAV_OBJTEXMODATTRIB;
	hint.scope = pObjNode;
	GetDocument()->UpdateAllViews(NULL, &hint);

	// Commit operation.
	OpTextureBrushManip *op =
		new OpTextureBrushManip(pOldXOffs, pOldYOffs, pOldRotAngs,
								pOldXScls, pOldYScls, pOldTexLocks);
	GetMainFrame()->CommitOperation(*op);

	// Clean up.
	delete [] pOldXOffs;
	delete [] pOldYOffs;
	delete [] pOldRotAngs;
	delete [] pOldXScls;
	delete [] pOldYScls;
	delete [] pOldTexLocks;
}

// Reset texture.
void QPropTexture::OnReset(wxCommandEvent &event)
{
	if(selectFace)
		ResetTextureFace();
	else
		ResetTextureBrushFaces();
}

void QPropTexture::ResetTextureFace()
{
	// Remember the old data.
	TransferDataFromWindow();
	oldXOffset = xoffset;
	oldYOffset = yoffset;
	oldXScale = xscale;
	oldYScale = yscale;
	oldRotAng = rotAng;
	bool oldTexLock = texLockCheck->GetValue();

	texLockCheck->SetValue(false);
	xoffset = yoffset = 0.0f;
	xscale = yscale = 1.0f;
	rotAng = 0.0f;


	TransferDataToWindow();
	UpdateDoc();

	Selector *selector = &GetMainFrame()->GetSelector();
	ASSERT(selector->GetSelectFaceIndex() == -1);

	OpTextureFaceManip *op =
			new OpTextureFaceManip(selector->GetSelectFaceIndex(),
					oldXOffset, oldYOffset, oldRotAng, oldXScale, oldYScale,
					oldTexLock);
	GetMainFrame()->CommitOperation(*op);
}

void QPropTexture::ResetTextureBrushFaces()
{
	Selector *selector = &GetMainFrame()->GetSelector();
	ASSERT(selector->GetNumMSelectedObjects() == 1);

	Object *object = selector->GetMSelectedObjects()[0].GetPtr();
	ASSERT(object->HasBrush());
	Geometry *brush = &object->GetBrush();

	int numFaces = brush->GetNumFaces();

	// TODO: Change the offsets into floats.
	int *oldXOffsets = new int[numFaces];
	int *oldYOffsets = new int[numFaces];
	float *oldRotAngs = new float[numFaces];
	float *oldXScale = new float[numFaces];
	float *oldYScale = new float[numFaces];
	bool *oldTexLocks = new bool[numFaces];

	FaceTex *faceTex;
	for(int i = 0; i < numFaces; i++)
	{
		faceTex = brush->GetFaceTexturePtr(i);
		faceTex->GetTInfo(oldXOffsets[i], oldYOffsets[i],
				oldRotAngs[i], oldXScale[i], oldYScale[i]);
		faceTex->SetTInfo(0, 0, 0.0f, 1.0f, 1.0f);
		// TODO: Read the texture lock.
		oldTexLocks[i] = false;
		faceTex->SetTexLock(false);
	}

	// Manual update the views, skipping self.
	QDocHint hint;
	hint.flags = DUAV_QVIEWS | DUAV_OBJTEXMODATTRIB;
	hint.scope = object;
	GetDocument()->UpdateAllViews(NULL, &hint);

	// Commit operation.
	OpTextureBrushManip *op =
			new OpTextureBrushManip(oldXOffsets, oldYOffsets, oldRotAngs,
					oldXScale, oldYScale, oldTexLocks);
	GetMainFrame()->CommitOperation(*op);

	// Clean up.
	delete [] oldXOffsets;
	delete [] oldYOffsets;
	delete [] oldRotAngs;
	delete [] oldXScale;
	delete [] oldYScale;
	delete [] oldTexLocks;
}

